home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-games-data / glchess / gtkui / dialogs.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  28.2 KB  |  758 lines

  1. # -*- coding: utf-8 -*-
  2. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  3. __license__ = 'GNU General Public License Version 2'
  4. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  5.  
  6. import os
  7. from gettext import gettext as _
  8.  
  9. import gobject
  10. import gtk
  11. import gtk.glade
  12. import gtk.gdk
  13.  
  14. import gtkui
  15. import glchess.ui
  16.  
  17. class GtkServerList:
  18.     __gui = None
  19.  
  20.     def __init__(self, gui):
  21.         self.__gui = gui
  22.         
  23.         self.__servers = []
  24.         view = gui.get_widget('server_list')
  25.         if view is not None:
  26.             store = gtk.ListStore(str, gobject.TYPE_PYOBJECT)
  27.             view.set_model(store)
  28.  
  29.             cell = gtk.CellRendererText()
  30.             column = gtk.TreeViewColumn('name', cell)
  31.             column.add_attribute(cell, 'text', 0)
  32.             view.append_column(column)
  33.         
  34.     def add(self, name, game):
  35.         """
  36.         """
  37.         view = self.__gui.get_widget('server_list')
  38.         if view is None:
  39.             return
  40.         model = view.get_model()
  41.         iter = model.append()
  42.         model.set(iter, 0, name)
  43.         model.set(iter, 1, game)
  44.         
  45.     def getSelected(self):
  46.         """
  47.         """
  48.         view = self.__gui.get_widget('server_list')
  49.         if view is None:
  50.             return None
  51.         selection = view.get_selection()
  52.         (model, iter) = selection.get_selected()
  53.         
  54.         if iter is None:
  55.             return None
  56.         else:
  57.             return model.get_value(iter, 1)
  58.         
  59.     def remove(self, game):
  60.         """
  61.         """
  62.         view = self.__gui.get_widget('server_list')
  63.         if view is None:
  64.             return
  65.         model = view.get_model()
  66.         
  67.         iter = model.get_iter_first()
  68.         while iter is not None:
  69.             if model.get_value(iter, 1) is game:
  70.                 break
  71.             iter = model.iter_next(iter)
  72.         
  73.         if iter is not None:
  74.             model.remove(iter)
  75.  
  76. class GtkNewGameDialog:
  77.     """
  78.     """    
  79.     def __init__(self, mainUI, aiModel, game = None):
  80.         """Constructor for a new game dialog.
  81.         
  82.         'mainUI' is the main UI.
  83.         'aiModel' is the AI models to use.
  84.         'game' is the game properties to use (ui.Game).
  85.         """
  86.         self.__mainUI = mainUI
  87.         self.game = game
  88.         
  89.         self.__checking = True
  90.         self.__customName = False
  91.  
  92.         # Load the UI
  93.         self.__gui = gtkui.loadGladeFile('new_game.glade', 'new_game_dialog')
  94.         self.__gui.signal_autoconnect(self)
  95.  
  96.         self.window = self.__gui.get_widget('new_game_dialog')
  97.         self.window.set_transient_for(mainUI.mainWindow)
  98.  
  99.         # Set style of error panel
  100.         mainUI.setTooltipStyle(self.__gui.get_widget('info_box'))
  101.         
  102.         # Make all the labels the same width
  103.         group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
  104.         i = 1
  105.         while True:
  106.             widget = self.__gui.get_widget('label%i' % i)
  107.             if widget is None:
  108.                 break
  109.             group.add_widget(widget)
  110.             i += 1
  111.  
  112.         # Make all the images the same width
  113.         group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
  114.         i = 1
  115.         while True:
  116.             widget = self.__gui.get_widget('image%i' % i)
  117.             if widget is None:
  118.                 break
  119.             group.add_widget(widget)
  120.             i += 1
  121.             
  122.         # Create model for game time
  123.         defaultTime = glchess.config.get('new_game_dialog/move_time')
  124.                   # Translators: Time Combo: There is no time limit
  125.         times = [(_('Unlimited'),       0),
  126.                   # Translators: Time Combo: Game will last one minute
  127.                  (_('One minute'),     60),
  128.                   # Translators: Time Combo: Game will last five minutes
  129.                  (_('Five minutes'),  300),
  130.                   # Translators: Time Combo: Game will last 30 minutes
  131.                  (_('30 minutes'),   1800),
  132.                   # Translators: Time Combo: Game will last one hour
  133.                  (_('One hour'),     3600),
  134.                   # Translators: Time Combo: User will configure game duration
  135.                  (_('Custom'),         -1)]
  136.         timeModel = gtk.ListStore(str, int)
  137.         activeIter = None
  138.         for (name, time) in times:
  139.             iter = timeModel.append()
  140.             if time == defaultTime:
  141.                 activeIter = iter
  142.             timeModel.set(iter, 0, name, 1, time)
  143.  
  144.         widget = self.__gui.get_widget('time_combo')
  145.         widget.set_model(timeModel)
  146.         if activeIter is None:
  147.             widget.set_active_iter(iter)
  148.         else:
  149.             widget.set_active_iter(activeIter)
  150.         cell = gtk.CellRendererText()
  151.         widget.pack_start(cell, False)
  152.         widget.add_attribute(cell, 'text', 0)
  153.  
  154.         model = gtk.ListStore(str, int)
  155.                   # Translators: Custom Time Combo: User specifying number of seconds for game duration
  156.         units = [(_('seconds'),  1),
  157.                   # Translators: Custom Time Combo: User specifying number of minutes for game duration
  158.                  (_('minutes'), 60),
  159.                   # Translators: Custom Time Combo: User specifying number of hours for game duration
  160.                  (_('hours'), 3600)]
  161.         for (name, multiplier) in units:
  162.             iter = model.append()
  163.             model.set(iter, 0, name, 1, multiplier)
  164.  
  165.         # FIXME: Handle time units
  166.         self.__gui.get_widget('custom_time_spin').set_value(defaultTime)
  167.  
  168.         widget = self.__gui.get_widget('custom_time_units_combo')
  169.         widget.set_model(model)
  170.         cell = gtk.CellRendererText()
  171.         widget.pack_start(cell, False)
  172.         widget.add_attribute(cell, 'text', 0)
  173.         widget.set_active(0)
  174.  
  175.         # Create the model for difficulty options
  176.         levelModel = gtk.ListStore(str, str, str)
  177.                   # Translators: AI Difficulty Combo: AI set to easy difficulty
  178.         levels = [('easy',   _('Easy'),   'weather-few-clouds'),
  179.                   # Translators: AI Difficulty Combo: AI set to normal diffuculty
  180.                   ('normal', _('Normal'), 'weather-overcast'),
  181.                   # Translators: AI Difficulty Combo: AI set to hard diffuculty
  182.                   ('hard',   _('Hard'),   'weather-storm')]
  183.         for (key, label, icon) in levels:
  184.             iter = levelModel.append()
  185.             levelModel.set(iter, 0, key, 1, icon, 2, label)
  186.  
  187.         # Set the difficulty settings
  188.         for name in ['black_difficulty_combo', 'white_difficulty_combo']:
  189.             widget = self.__gui.get_widget(name)
  190.             if widget is None:
  191.                 continue
  192.             
  193.             widget.set_model(levelModel)
  194.             
  195.             cell = gtk.CellRendererPixbuf()
  196.             widget.pack_start(cell, False)
  197.             widget.add_attribute(cell, 'icon-name', 1)
  198.             
  199.             cell = gtk.CellRendererText()
  200.             widget.pack_start(cell, False)
  201.             widget.add_attribute(cell, 'text', 2)
  202.             
  203.             widget.set_active(1)
  204.  
  205.         # Make all the AI combo boxes use one list of AI types
  206.         firstAIIndex = min(1, len(aiModel))
  207.         for (name, index) in [('white_type_combo', 0), ('black_type_combo', firstAIIndex)]:
  208.             widget = self.__gui.get_widget(name)
  209.             if widget is None:
  210.                 continue
  211.             
  212.             widget.set_model(aiModel)
  213.             
  214.             cell = gtk.CellRendererPixbuf()
  215.             widget.pack_start(cell, False)
  216.             widget.add_attribute(cell, 'icon-name', 1)
  217.             
  218.             cell = gtk.CellRendererText()
  219.             widget.pack_start(cell, False)
  220.             widget.add_attribute(cell, 'text', 2)
  221.             
  222.             widget.set_active(index)
  223.  
  224.         # Configure AIs
  225.         try:
  226.             whiteType = glchess.config.get('new_game_dialog/white/type')
  227.             whiteLevel = glchess.config.get('new_game_dialog/white/difficulty')
  228.             blackType = glchess.config.get('new_game_dialog/black/type')
  229.             blackLevel = glchess.config.get('new_game_dialog/black/difficulty')
  230.         except glchess.config.Error:
  231.             pass
  232.         else:
  233.             self.__setCombo('white_type_combo', whiteType)
  234.             self.__setCombo('white_difficulty_combo', whiteLevel)
  235.             self.__setCombo('black_type_combo', blackType)
  236.             self.__setCombo('black_difficulty_combo', blackLevel)
  237.  
  238.         # Use supplied settings
  239.         errors = []
  240.         g = self.game
  241.         if g is not None:
  242.             self.__gui.get_widget('game_name_entry').set_text(g.name)
  243.             self.__customName = True
  244.             # Translators: Error displayed when unable to load a game due to
  245.             # the require game engine not being available. %s is replaced with
  246.             # the name of the missing engine.
  247.             noEngineFormat = _('Unable to find %s engine')
  248.             if not self.__setCombo('white_type_combo', g.white.type):
  249.                 errors.append(noEngineFormat % repr(g.white.type))
  250.             self.__setCombo('white_difficulty_combo', g.white.level)
  251.             if not self.__setCombo('black_type_combo', g.black.type):
  252.                 errors.append(noEngineFormat % repr(g.black.type))
  253.             self.__setCombo('black_difficulty_combo', g.black.level)
  254.             # TODO: Others
  255.             
  256.             # Change title for loaded games
  257.             if g.path is not None:
  258.                 # Translators: New Game Dialog: Title of the dialog when continuing a loaded game
  259.                 self.window.set_title(_('Configure loaded game (%i moves)') % len(g.moves))
  260.  
  261.         # Display warning if missing the AIs
  262.         if len(errors) > 0:
  263.             # Translators: New Game Dialog: Title of error box when loaded game had AI engines missing
  264.             self.__gui.get_widget('info_title_label').set_markup('<big><b>%s</b></big>' % _('Game settings changed'))
  265.             self.__gui.get_widget('info_description_label').set_markup('<i>%s</i>' % '\n'.join(errors))
  266.             self.__gui.get_widget('info_box').show()
  267.  
  268.         # Show the dialog
  269.         self.window.present()
  270.         self.__checking = False
  271.         self.__testReady()
  272.  
  273.     # Private methods
  274.     
  275.     def __setCombo(self, comboName, key):
  276.         """
  277.         """
  278.         widget = self.__gui.get_widget(comboName)
  279.         iter = self.__getIter(widget.get_model(), key)
  280.         if iter is None:
  281.             return False
  282.         else:
  283.             widget.set_active_iter(iter)
  284.             return True
  285.  
  286.     def __getIter(self, model, key, default = None):
  287.         """
  288.         """
  289.         iter = model.get_iter_first()
  290.         while iter:
  291.             if model.get_value(iter, 0) == key:
  292.                 return iter
  293.  
  294.             iter = model.iter_next(iter)
  295.         return default
  296.  
  297.     def __getComboData(self, comboBox, index):
  298.         """
  299.         """
  300.         model = comboBox.get_model()
  301.         iter = comboBox.get_active_iter()
  302.         if iter is None:
  303.             return None
  304.         
  305.         data = model.get(iter, index)
  306.         return data[0]
  307.  
  308.     def __testReady(self):
  309.         if self.__checking:
  310.             return
  311.         self.__checking = True
  312.         ready = True
  313.  
  314.         # Must have a name for the game
  315.         if self.__customName:
  316.             name = self.__gui.get_widget('game_name_entry').get_text()
  317.             if len(name) == 0:
  318.                 # Next time something changes generate a name
  319.                 self.__customName = False
  320.                 ready = False
  321.  
  322.         # Name the game based on the players
  323.         else:
  324.             whiteName = self.__getComboData(self.__gui.get_widget('white_type_combo'), 2)
  325.             blackName = self.__getComboData(self.__gui.get_widget('black_type_combo'), 2)
  326.             # Translators: Default name for a new game. %(white) and %(black) are substituted for the names of the white and black players.
  327.             format = _('%(white)s versus %(black)s')
  328.             self.__gui.get_widget('game_name_entry').set_text(format % {'white': whiteName, 'black': blackName})
  329.  
  330.         # Disable difficulty for human players
  331.         whiteType = self.__getComboData(self.__gui.get_widget('white_type_combo'), 0)
  332.         blackType = self.__getComboData(self.__gui.get_widget('black_type_combo'), 0)
  333.         self.__gui.get_widget('white_difficulty_combo').set_sensitive(whiteType != '')
  334.         self.__gui.get_widget('black_difficulty_combo').set_sensitive(blackType != '')
  335.  
  336.         # Can only click OK if have enough information
  337.         self.__gui.get_widget('start_button').set_sensitive(ready)
  338.         self.__checking = False
  339.         
  340.     def __startGame(self):
  341.         game = self.game
  342.         if game is None:
  343.             game = glchess.ui.Game()
  344.         game.name = self.__gui.get_widget('game_name_entry').get_text()
  345.         game.allowSpectators = True
  346.         
  347.         # Get the players
  348.         game.white.type  = self.__getComboData(self.__gui.get_widget('white_type_combo'), 0)
  349.         if game.white.type == '':
  350.             # Translators: Default name for the white player
  351.             game.white.name = _('White')
  352.         else:
  353.             game.white.name = self.__getComboData(self.__gui.get_widget('white_type_combo'), 2)
  354.         game.white.level = self.__getComboData(self.__gui.get_widget('white_difficulty_combo'), 0)
  355.         game.black.type  = self.__getComboData(self.__gui.get_widget('black_type_combo'), 0)
  356.         if game.black.type == '':
  357.             # Translators: Default name for the black player
  358.             game.black.name = _('Black')
  359.         else:
  360.             game.black.name = self.__getComboData(self.__gui.get_widget('black_type_combo'), 2)
  361.         game.black.level = self.__getComboData(self.__gui.get_widget('black_difficulty_combo'), 0)
  362.  
  363.         game.duration = self.__getGameDuration()
  364.             
  365.         # Save properties
  366.         glchess.config.set('new_game_dialog/move_time', game.duration)
  367.         glchess.config.set('new_game_dialog/white/type', game.white.type)
  368.         glchess.config.set('new_game_dialog/white/difficulty', game.white.level)
  369.         glchess.config.set('new_game_dialog/black/type', game.black.type)
  370.         glchess.config.set('new_game_dialog/black/difficulty', game.black.level)
  371.  
  372.         # Inform the child class
  373.         self.__mainUI.feedback.onGameStart(game)
  374.     
  375.     def __getGameDuration(self):
  376.         duration = self.__getComboData(self.__gui.get_widget('time_combo'), 1)
  377.         if duration < 0:
  378.             multiplier = self.__getComboData(self.__gui.get_widget('custom_time_units_combo'), 1)
  379.             duration = self.__gui.get_widget('custom_time_spin').get_value_as_int() * multiplier
  380.         return duration
  381.         
  382.     # Gtk+ signal handlers
  383.     
  384.     def _on_game_name_edited(self, widget):
  385.         """Gtk+ callback"""
  386.         if self.__checking:
  387.             return
  388.         self.__customName = True
  389.         self.__testReady()
  390.  
  391.     def _on_time_changed(self, widget):
  392.         """Gtk+ callback"""
  393.         time = self.__getComboData(widget, 1)
  394.         w = self.__gui.get_widget('custom_time_box')
  395.         if time < 0:
  396.             w.show()
  397.         else:
  398.             w.hide()
  399.  
  400.     def _on_properties_changed(self, widget, *data):
  401.         """Gtk+ callback"""
  402.         self.__testReady()
  403.  
  404.     def _on_response(self, dialog, responseId):
  405.         """Gtk+ callback"""
  406.         if responseId == gtk.RESPONSE_OK:
  407.             self.__startGame()
  408.         dialog.destroy()
  409.         if self.__mainUI.newGameDialog is self:
  410.             self.__mainUI.newGameDialog = None
  411.  
  412. class GtkLoadGameDialog:
  413.     """
  414.     """    
  415.     def __init__(self, mainUI):
  416.         """
  417.         """
  418.         self.__mainUI = mainUI
  419.         
  420.         # Load the UI
  421.         self.__gui = gtkui.loadGladeFile('load_game.glade')
  422.         self.__gui.signal_autoconnect(self)
  423.         
  424.         self.window = self.__gui.get_widget('game_load_dialog')
  425.         self.window.set_transient_for(mainUI.mainWindow)
  426.         
  427.         # Set style of error panel
  428.         mainUI.setTooltipStyle(self.__gui.get_widget('error_box'))
  429.         
  430.         fileChooser = self.__gui.get_widget('filechooserwidget')
  431.         
  432.         try:
  433.             directory = str(glchess.config.get('load_directory'))
  434.         except glchess.config.Error:
  435.             pass
  436.         else:
  437.             fileChooser.set_current_folder(directory)
  438.  
  439.         # Filter out non PGN files by default
  440.         pgnFilter = gtk.FileFilter()
  441.         # Translators: Load Game Dialog: Name of filter to show only PGN files
  442.         pgnFilter.set_name(_('PGN files'))
  443.         pgnFilter.add_pattern('*.pgn')
  444.         fileChooser.add_filter(pgnFilter)
  445.         
  446.         allFilter = gtk.FileFilter()
  447.         # Translators: Load Game Dialog: Name of filter to show all files
  448.         allFilter.set_name(_('All files'))
  449.         allFilter.add_pattern('*')
  450.         fileChooser.add_filter(allFilter)
  451.         
  452.         self.window.present()
  453.             
  454.     def _on_file_activated(self, widget):
  455.         """Gtk+ callback"""
  456.         self._on_response(self.window, gtk.RESPONSE_OK)
  457.  
  458.     def _on_response(self, dialog, responseId):
  459.         """Gtk+ callback"""
  460.         chooser = self.__gui.get_widget('filechooserwidget')
  461.  
  462.         if responseId == gtk.RESPONSE_OK or responseId == gtk.RESPONSE_YES:
  463.             folder = chooser.get_current_folder()
  464.             if folder is not None:
  465.                 glchess.config.set('load_directory', folder)
  466.  
  467.             fileName = self.__gui.get_widget('filechooserwidget').get_filename()
  468.             if fileName is None:
  469.                 # Translators: Load Game Dialog: Message displayed when no file is selected
  470.                 error = _('Please select a file to load')
  471.             else:
  472.                 error = self.__mainUI.feedback.loadGame(fileName, responseId == gtk.RESPONSE_YES)
  473.  
  474.             if error is not None:
  475.                 self.firstExpose = True
  476.                 self.__gui.get_widget('error_box').show()
  477.                 # Translators: Title of error box when unable to load game
  478.                 self.__gui.get_widget('error_title_label').set_markup('<big><b>%s</b></big>' % _('Unabled to load game'))
  479.                 self.__gui.get_widget('error_description_label').set_markup('<i>%s</i>' % error)
  480.                 return
  481.  
  482.         dialog.destroy()
  483.         if self.__mainUI.loadGameDialog is self:
  484.             self.__mainUI.loadGameDialog = None
  485.         
  486. class GtkSaveGameDialog:
  487.     """
  488.     """
  489.     def __init__(self, mainUI, view, path = None):
  490.         """
  491.         """
  492.         self.__mainUI = mainUI
  493.         self.__view = view
  494.         
  495.         # Load the UI
  496.         self.__gui = gtkui.loadGladeFile('save_game.glade')
  497.         self.__gui.signal_autoconnect(self)
  498.         
  499.         # Set style of error panel
  500.         mainUI.setTooltipStyle(self.__gui.get_widget('error_box'))
  501.  
  502.         self.window = self.__gui.get_widget('save_dialog')
  503.         self.window.set_transient_for(mainUI.mainWindow)
  504.         chooser = self.__gui.get_widget('filechooser')
  505.         
  506.         try:
  507.             directory = str(glchess.config.get('save_directory'))
  508.         except glchess.config.Error:
  509.             pass
  510.         else:
  511.             chooser.set_current_folder(directory)       
  512.         
  513.         # Filter out non PGN files by default
  514.         pgnFilter = gtk.FileFilter()
  515.         pgnFilter.set_name('PGN files')
  516.         pgnFilter.add_pattern('*.pgn')
  517.         chooser.add_filter(pgnFilter)
  518.         
  519.         allFilter = gtk.FileFilter()
  520.         allFilter.set_name('All files')
  521.         allFilter.add_pattern('*')
  522.         chooser.add_filter(allFilter)
  523.         
  524.         if path is not None:
  525.             chooser.set_current_name(path)
  526.         
  527.     def _on_file_activated(self, widget):
  528.         """Gtk+ callback"""
  529.         self._on_response(self.window, gtk.RESPONSE_OK)
  530.  
  531.     def __setError(self, title, content):
  532.         """
  533.         """
  534.         self.firstExpose = True
  535.         self.__gui.get_widget('error_box').show()
  536.         self.__gui.get_widget('error_title_label').set_markup('<big><b>%s</b></big>' % title)
  537.         self.__gui.get_widget('error_description_label').set_markup('<i>%s</i>' % content)
  538.         
  539.     def _on_response(self, dialog, responseId):
  540.         """Gtk+ callback"""
  541.         chooser = self.__gui.get_widget('filechooser')
  542.         
  543.         if responseId == gtk.RESPONSE_OK:
  544.             # Append .pgn to the end if not provided
  545.             fname = chooser.get_filename()
  546.             if fname is None:
  547.                 # Translators: Save Game Dialog: Error displayed when no file name entered
  548.                 self.__setError(_('Please enter a file name'), '')
  549.                 return
  550.             if fname[-4:].lower() != '.pgn':
  551.                 fname += '.pgn'
  552.  
  553.             # Save the directory we used
  554.             folder = chooser.get_current_folder()
  555.             if folder is not None:
  556.                 glchess.config.set('save_directory', folder)
  557.  
  558.             error = self.__mainUI._saveView(self.__view, fname)
  559.             if error is not None:
  560.                 # Translators: Save Game Dialog: Error title when unable to save game
  561.                 self.__setError(_('Unabled to save game'), error)
  562.                 return
  563.         else:
  564.             self.__mainUI._saveView(self.__view, None)
  565.  
  566.         dialog.destroy()
  567.  
  568. class GtkPreferencesDialog:
  569.     """
  570.     """
  571.     def __init__(self, mainUI):
  572.         """Constructor for the preferences dialog.
  573.         
  574.         'mainUI' is the main UI.
  575.         """
  576.         # Load the UI
  577.         self.__gui = gtkui.loadGladeFile('preferences.glade', 'preferences')
  578.         self.__gui.signal_autoconnect(self)
  579.         
  580.         self.__gui.get_widget('preferences').set_transient_for(mainUI.mainWindow)
  581.  
  582.         # Make model for move format
  583.         moveModel = gtk.ListStore(str, str)
  584.         widget = self.__gui.get_widget('move_format_combo')
  585.         widget.set_model(moveModel)
  586.                         # Translators: Move Format Combo: Moves shown in human descriptive notation
  587.         move_formats = [('human', _('Human')),
  588.                         # Translators: Move Format Combo: Moves shown in standard algebraic notation (SAN)
  589.                         ('san', _('Standard Algebraic')),
  590.                         # Translators: Move Format Combo: Moves shown in standard figurine algebraic notation (FAN)
  591.                         ('fan', _('Figurine')),
  592.                         # Translators: Move Format Combo: Moves shown in long algebraic notation (LAN)
  593.                         ('lan', _('Long Algebraic'))]
  594.         for (key, label) in move_formats:
  595.             iter = moveModel.append()
  596.             moveModel.set(iter, 0, label, 1, key)
  597.  
  598.         # Make model for board orientation
  599.         boardModel = gtk.ListStore(str, str)
  600.         widget = self.__gui.get_widget('board_combo')
  601.         widget.set_model(boardModel)
  602.                      # Translators: Board Side Combo: Camera will face white player's side
  603.         view_list = [('white', _('White Side')),
  604.                      # Translators: Board Side Combo: Camera will face black player's side
  605.                      ('black', _('Black Side')),
  606.                      # Translators: Board Side Combo: Camera will face human player's side
  607.                      ('human', _('Human Side')),
  608.                      # Translators: Board Side Combo: Camera will face current player's side
  609.                      ('current', _('Current Player'))]
  610.         for (key, label) in view_list:
  611.             iter = boardModel.append()
  612.             boardModel.set(iter, 0, label, 1, key)
  613.  
  614.         # Make modelfor promotion type
  615.         promotionModel = gtk.ListStore(str, str)
  616.         widget = self.__gui.get_widget('promotion_type_combo')
  617.         widget.set_model(promotionModel)
  618.                           # Translators: Promotion Combo: Promote to a queen. Do not translate the 'chess-piece|' text.
  619.         promotion_list = [('queen',  _('chess-piece|Queen')),
  620.                           # Translators: Promotion Combo: Promote to a knight. Do not translate the 'chess-piece|' text.
  621.                           ('knight', _('chess-piece|Knight')),
  622.                           # Translators: Promotion Combo: Promote to a rook. Do not translate the 'chess-piece|' text.
  623.                           ('rook',   _('chess-piece|Rook')),
  624.                           # Translators: Promotion Combo: Promote to a bishop. Do not translate the 'chess-piece|' text.
  625.                           ('bishop', _('chess-piece|Bishop'))]
  626.         for (key, label) in promotion_list:
  627.             try:
  628.                 label = label.split('|', 1)[1]
  629.             except IndexError:
  630.                 pass
  631.             iter = promotionModel.append()
  632.             promotionModel.set(iter, 0, label, 1, key)
  633.             
  634.         # Watch for config changes
  635.         for key in ['show_3d', 'show_3d_smooth', 'show_toolbar', 'show_history', 
  636.                     'show_move_hints', 'show_numbering',
  637.                     'move_format', 'board_view', 'promotion_type']:
  638.             glchess.config.watch(key, self.__applyConfig)
  639.             try:
  640.                 value = glchess.config.get(key)
  641.             except glchess.config.Error:
  642.                 pass
  643.             else:
  644.                 self.__applyConfig(key, value)
  645.         
  646.     def __applyConfig(self, name, value):
  647.         """
  648.         """        
  649.         if name == 'show_3d':
  650.             self.__gui.get_widget('show_3d').set_active(value)
  651.             self.__gui.get_widget('show_3d_smooth').set_sensitive(value)            
  652.  
  653.         elif name == 'show_3d_smooth':
  654.             self.__gui.get_widget('show_3d_smooth').set_active(value)
  655.             
  656.         elif name == 'show_toolbar':
  657.             self.__gui.get_widget('show_toolbar').set_active(value)
  658.                 
  659.         elif name == 'show_history':
  660.             self.__gui.get_widget('show_history').set_active(value)
  661.  
  662.         elif name == 'show_move_hints':
  663.             self.__gui.get_widget('show_move_hints').set_active(value)
  664.  
  665.         elif name == 'show_numbering':
  666.             self.__gui.get_widget('show_numbering').set_active(value)
  667.  
  668.         elif name == 'move_format':
  669.             widget = self.__gui.get_widget('move_format_combo')
  670.             for row in widget.get_model():
  671.                 if row[1] == value:
  672.                     widget.set_active_iter(row.iter)
  673.                 
  674.         elif name == 'promotion_type':
  675.             widget = self.__gui.get_widget('promotion_type_combo')
  676.             for row in widget.get_model():
  677.                 if row[1] == value:
  678.                     widget.set_active_iter(row.iter)
  679.     
  680.         elif name == 'board_view':
  681.             widget = self.__gui.get_widget('board_combo')
  682.             for row in widget.get_model():
  683.                 if row[1] == value:
  684.                     widget.set_active_iter(row.iter)
  685.  
  686.         else:
  687.             assert(False), 'Unknown config item: %s' % name
  688.  
  689.     def setVisible(self, isVisible):
  690.         window = self.__gui.get_widget('preferences')
  691.         if isVisible:
  692.             window.present()
  693.         else:
  694.             window.hide()
  695.  
  696.     def _on_response(self, dialog, responseId):
  697.         """Gtk+ callback"""
  698.         self.setVisible(False)
  699.         
  700.     def _on_delete(self, dialog, event):
  701.         """Gtk+ callback"""
  702.         self.setVisible(False)
  703.         return True
  704.  
  705.     def _on_move_format_combo_changed(self, widget):
  706.         """Gtk+ callback"""
  707.         model = widget.get_model()
  708.         iter = widget.get_active_iter()
  709.         if iter is None:
  710.             return
  711.         data = model.get(iter, 1)
  712.         if data[0] is not None:
  713.             glchess.config.set('move_format', data[0])
  714.  
  715.     def _on_board_combo_changed(self, widget):
  716.         """Gtk+ callback"""
  717.         model = widget.get_model()
  718.         iter = widget.get_active_iter()
  719.         if iter is None:
  720.             return
  721.         data = model.get(iter, 1)
  722.         if data[0] is not None:
  723.             glchess.config.set('board_view', data[0])
  724.  
  725.     def _on_promotion_type_combo_changed(self, widget):
  726.         """Gtk+ callback"""
  727.         model = widget.get_model()
  728.         iter = widget.get_active_iter()
  729.         if iter is None:
  730.             return
  731.         data = model.get(iter, 1)
  732.         if data[0] is not None:
  733.             glchess.config.set('promotion_type', data[0])
  734.  
  735.     def _on_3d_view_activate(self, widget):
  736.         """Gtk+ callback"""
  737.         glchess.config.set('show_3d', widget.get_active())
  738.         
  739.     def _on_3d_smooth_toggled(self, widget):
  740.         """Gtk+ callback"""
  741.         glchess.config.set('show_3d_smooth', widget.get_active())        
  742.  
  743.     def _on_show_toolbar_activate(self, widget):
  744.         """Gtk+ callback"""
  745.         glchess.config.set('show_toolbar', widget.get_active())
  746.  
  747.     def _on_show_history_activate(self, widget):
  748.         """Gtk+ callback"""
  749.         glchess.config.set('show_history', widget.get_active())
  750.  
  751.     def _on_move_hints_activate(self, widget):
  752.         """Gtk+ callback"""
  753.         glchess.config.set('show_move_hints', widget.get_active())
  754.  
  755.     def _on_board_numbering_activate(self, widget):
  756.         """Gtk+ callback"""
  757.         glchess.config.set('show_numbering', widget.get_active())
  758.